interface _CU_ {
    /**
     * 比较两个对象是否一致
     * @param obj1 对象1
     * @param obj2 对象2
     * @param converter 转换器
     * @param sames 视为相同的字符串
     * @returns true: 一致; false: 不一致;
     */
    isEqual(obj1: any, obj2: any, converter?: Tstring_converters, sames?: Array<string>): boolean;
}
{
    const converter_symbol = (obj: any) => { return String(obj.description).trim(); };
    const converter_bigint = (obj: any) => { return String(obj); };
    const converter_string = (obj: any) => { return obj.trim(); };

    const isEqual = U.F.overloading('Utils-isEqual',
        {
            types: [Etype.obj, Etype.obj, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                const keys1 = Object.keys(obj1);
                const keys2 = Object.keys(obj2);
                if (keys1.length !== keys2.length) { return false; }
                if (!isEqual(new Set(keys1), new Set(keys2), converter, sames)) { return false; }
                for (let i = 0; i < keys1.length; i++) {
                    const key = keys1[i];
                    if (!isEqual(obj1[key], obj2[key], converter, sames)) { return false; }
                }
                return true;
            }
        },
        {
            types: [Etype.obj, Etype.Map, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                const keys1 = Object.keys(obj1);
                if (keys1.length !== obj2.size) { return false; }
                if (!isEqual(new Set(keys1), new Set(obj2.keys()), converter, sames)) { return false; }
                for (let i = 0; i < keys1.length; i++) {
                    const key = keys1[i];
                    if (!isEqual(obj1[key], obj2.get(key), converter, sames)) { return false; }
                }
                return true;
            }
        },
        {
            types: [Etype.array, Etype.array, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                if (obj1.length !== obj2.length) { return false; }
                const keys1 = Object.keys(obj1);
                const keys2 = Object.keys(obj2);
                if (keys1.length !== keys2.length) { return false; }
                if (!isEqual(new Set(keys1), new Set(keys2), converter, sames)) { return false; }
                for (let i = 0; i < keys1.length; i++) {
                    const key = keys1[i];
                    if (!isEqual(obj1[key], obj2[key], converter, sames)) { return false; }
                }
                return true;
            }
        },
        {
            types: [Etype.Map, Etype.Map, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                if (obj1.size !== obj2.size) { return false; }
                for (const [key, value] of obj1) {
                    if (!obj2.has(key)) { return false; }
                    if (!isEqual(value, obj2.get(key), converter, sames)) { return false; }
                }
                return true;
            }
        },
        {
            types: [Etype.Map, Etype.obj, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                return isEqual(obj2, obj1, converter, sames);
            }
        },
        {
            types: [Etype.Set, Etype.Set, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                if (obj1.size !== obj2.size) { return false; }
                obj2 = new Set([...obj2]);
                for (const value1 of obj1) {
                    let flag = false;
                    for (const value2 of obj2) {
                        if (isEqual(value1, value2, converter, sames)) {
                            flag = true;
                            obj2.delete(value2);
                            break;
                        }
                    }
                    if (!flag) { return false; }
                }
                return obj2.size === 0;
            }
        },
        {
            types: [U.F.argType.any, U.F.argType.any, Etype.obj, Etype.array],
            fn: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
                const s1 = U.stringValue(obj1, converter);
                const s2 = U.stringValue(obj2, converter);

                if (s1 == s2) { return true; }

                if (sames.length >= 2) {
                    return sames.indexOf(s1) >= 0 && sames.indexOf(s2) >= 0;
                } else {
                    return false;
                }
            }
        }
    );

    Object.defineProperty(_CU_.prototype, 'isEqual', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: (obj1: any, obj2: any, converter: any, sames: Array<string>) => {
            if (obj1 === obj2) { return true; }

            converter = converter || {};

            converter[Etype.symbol] = converter[Etype.symbol] || converter_symbol;
            converter[Etype.bigint] = converter[Etype.bigint] || converter_bigint;
            converter[Etype.string] = converter[Etype.string] || converter_string;

            sames = sames || ['undefined', '', '0', 'null', 'false'];

            return isEqual(obj1, obj2, converter, sames);
        }
    });
}
